﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using AsNum.Common.EntLib.Validation.Adapters;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation.Instrumentation;

namespace AsNum.Common.EntLib.Validation {
    /// <summary>
    /// 
    /// </summary>
    public static class ValidationHelper {


        /// <summary>
        /// 获取客户端验证规则
        /// </summary>
        /// <param name="targetType"></param>
        /// <param name="ruleSet"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public static string GetClientRules(Type targetType , string ruleSet , Dictionary<string , Tuple<string, string>> map) {
            var rules = GetRules ( targetType , ruleSet , map );
            return FormatExport ( rules , map , true );
        }






        /// <summary>
        /// 从配置文件中获取 ValidationSetting
        /// </summary>
        /// <returns></returns>
        private static ValidationSettings GetValidationSettings() {
            //GetSection 只能读出存在于 web.config 里的，不能读取 redirectSections 里的
            //var settings = (ValidationSettings)ConfigurationManager.GetSection(ValidationSettings.SectionName);
            var source = new SystemConfigurationSource ( );
            var provider = ValidationInstrumentationProvider.FromConfigurationSource ( source );
            return ValidationSettings.TryGet ( source , provider );
        }


        /// <summary>
        /// 从配置文件中,获取对应 Validation 适配器, 返回 以 property / Field 分组的规则集合
        /// </summary>
        /// <param name="typeFullName"></param>
        /// <param name="ruleSet"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        internal static Dictionary<string , List<BaseClientAdapter>> GetRuleGroups(string typeFullName , string ruleSet , Dictionary<string , Tuple<string, string>> map) {
            var settings = GetValidationSettings ( );
            if ( settings != null ) {
                //找到配置中和要验证的类型匹配的规则
                var type = settings.Types.Where ( t => t.Name == typeFullName ).FirstOrDefault ( );
                if ( type != null ) {
                    var data = type.Rulesets.Where ( t => t.Name == ruleSet ).FirstOrDefault ( );
                    if ( data != null ) {
                        Dictionary<string , List<BaseClientAdapter>> adapters = new Dictionary<string , List<BaseClientAdapter>> ( );

                        //data.Properties.Where(p => map.ContainsValue(p.Name)).ToList().ForEach(p => {
                        //var a = data.Properties.Select ( p => p.Validators );
                        data.Properties.ForEach ( p => {
                            var list = new List<BaseClientAdapter> ( );
                            p.Validators.ForEach ( v => {
                                list.Add ( ClientAdapterFactory.GetAdapter ( v , map ) );
                            } );

                            adapters.Add ( string.Format ( "{0}.{1}" , typeFullName , p.Name ) , list );
                        } );

                        //data.Fields.Where(f => map.ContainsValue(f.Name)).ToList().ForEach((f) => {
                        //var b = data.Fields.Select ( f => f.Validators );
                        data.Fields.ForEach ( (f) => {
                            var list = new List<BaseClientAdapter> ( );
                            f.Validators.ForEach ( (v) => {
                                list.Add ( ClientAdapterFactory.GetAdapter ( v , map ) );
                            } );
                            adapters.Add ( f.Name , list );
                        } );

                        return adapters;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 从配置文件中,获取对应 Validation 适配器, 返回 以 property / Field 分组的规则集合
        /// </summary>
        /// <param name="targetType">要验证的类型</param>
        /// <param name="ruleSet">规则名</param>
        /// <returns>以 property / Field 分组的规则集合</returns>
        private static Dictionary<string , List<BaseClientAdapter>> GetRuleGroups(Type targetType , string ruleSet , Dictionary<string , Tuple<string , string>> map) {
            return GetRuleGroups ( targetType.FullName , ruleSet , map );
        }

        /// <summary>
        /// 同一类型只可能出现一次,同一类型下的 ruleSet 名称不能重复
        /// </summary>
        /// <param name="ruleset"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        internal static string FindRulesetsByName(string ruleset , string type) {
            var settings = GetValidationSettings ( );
            if ( settings != null ) {
                return settings.Types.Where ( t => t.Name == type && t.Rulesets.Contains ( ruleset ) ).Select ( t => t.Name ).FirstOrDefault ( );
            }
            return null;
        }





        /// <summary>
        /// 
        /// </summary>
        /// <param name="targetType"></param>
        /// <param name="ruleSet"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        internal static Dictionary<string , Tuple<bool , List<DetailTree>>> GetRules(Type targetType , string ruleSet , Dictionary<string , Tuple<string , string>> map) {
            //以 property / Field 分组的规则集合
            var ruleGroups = GetRuleGroups ( targetType , ruleSet , map );

            Dictionary<string , List<BaseClientAdapter>> subRuleGroups = new Dictionary<string , List<BaseClientAdapter>> ( );
            foreach ( var key in ruleGroups.Keys ) {
                var adapters = ruleGroups[key];
                #region 提取 Object Validator
                var objectAdapters = adapters.Where ( adapter => adapter.RuleType == RuleTypes.Object );
                if ( objectAdapters.Count ( ) > 0 ) {
                    var subTypeFullname = "";
                    var keyName = key.Split ( '.' ).Last ( );
                    var property = targetType.GetProperty ( keyName );
                    if ( property != null )
                        subTypeFullname = property.PropertyType.FullName;
                    else {
                        var field = targetType.GetField ( keyName );
                        if ( field != null )
                            subTypeFullname = field.FieldType.FullName;
                    }

                    var subAdapters = objectAdapters.Select ( o => ( ( ObjectClientAdapter )o ).GetSubAdapters ( subTypeFullname ) ).ToList ( );
                    subAdapters.ForEach ( (sub) => {
                        sub.ToList ( ).ForEach ( (s) => {
                            if ( !subRuleGroups.ContainsKey ( s.Key ) )
                                subRuleGroups.Add ( s.Key , s.Value );
                        } );
                    } );

                    adapters.RemoveAll ( (adapter) => adapter.RuleType == RuleTypes.Object );
                #endregion
                }
            }

            #region 合并 Object Validator 到主线上
            foreach ( var a in subRuleGroups.Keys ) {
                if ( !ruleGroups.ContainsKey ( a ) )
                    ruleGroups.Add ( a , subRuleGroups[a] );
            }
            #endregion

            return GetRules ( ruleGroups , map );
        }


        internal static Dictionary<string , Tuple<bool , List<DetailTree>>> GetRules(string typeFullName , string ruleSet , Dictionary<string , Tuple<string , string>> map) {
            var ruleGroups = GetRuleGroups ( typeFullName , ruleSet , map );
            return GetRules ( ruleGroups , map );
        }



        private static DetailTree GetDetailTree(BaseClientAdapter adapter) {
            if ( adapter.RuleType == RuleTypes.NotNull )
                return null;
            return new DetailTree ( ) {
                Details = adapter.Details ,
                SubRuleTrees = adapter.SubAdapters != null ? adapter.SubAdapters.Select ( sub => GetDetailTree ( sub ) ) : null
            };
        }

        /// <summary>
        /// 是否可为空,如果可为空,返回 true
        /// </summary>
        /// <param name="adapters"></param>
        /// <returns></returns>
        private static bool Nullable(IEnumerable<BaseClientAdapter> adapters) {

            var b1 = adapters.Where ( a => a.RuleType == RuleTypes.NotNull && a.Negated ).Count ( ) > 0;
            if ( b1 == true )
                return true;
            var b2 = adapters.Where ( a => a.SubAdapters != null && Nullable ( a.SubAdapters ) ).Count ( ) > 0;
            return b1 || b2;
        }


        internal static Dictionary<string , Tuple<bool , List<DetailTree>>> GetRules(Dictionary<string , List<BaseClientAdapter>> ruleGroups , Dictionary<string , Tuple<string , string>> map) {
            Dictionary<string , Tuple<bool , List<DetailTree>>> treeNodes = new Dictionary<string , Tuple<bool , List<DetailTree>>> ( );
            if ( ruleGroups != null ) {
                //key为 RuleSet 里的相关 Property / Field
                foreach ( var key in ruleGroups.Keys ) {
                    Tuple<bool , List<DetailTree>> treeNode;
                    List<DetailTree> trees = new List<DetailTree> ( );
                    var adapters = ruleGroups[key];
                    var nullable = Nullable ( adapters );
                    adapters.ForEach ( (adapter) => {
                        var detail = GetDetailTree ( adapter );
                        if ( detail != null )
                            trees.Add ( detail );
                    } );

                    treeNode = Tuple.Create ( nullable , trees );

                    treeNodes.Add ( key , treeNode );
                }
            }

            return treeNodes;
        }



        internal static string FormatDetailTree(DetailTree tree , string control, string label , bool nullable , bool top = false) {
            if ( tree == null )
                return null;
            var details = tree.Details;
            if ( top ) {

                if ( string.IsNullOrWhiteSpace ( label ) )
                    label = control;

                if ( details.ContainsKey ( RuleKeys.Name ) )
                    details[RuleKeys.Name] = string.Format ( "'{0}'" , control );
                else
                    details.Add ( RuleKeys.Name , string.Format ( "'{0}'" , control ) );

                if ( details.ContainsKey ( RuleKeys.Label ) )
                    details[RuleKeys.Label] = string.Format ( "'{0}'" , label );
                else
                    details.Add ( RuleKeys.Label , string.Format ( "'{0}'" , label ) );

                if ( !details.ContainsKey ( RuleKeys.Required ) )
                    details.Add ( RuleKeys.Required , ( !nullable ).JsBool ( ) );
                else
                    details[RuleKeys.Required] = ( !nullable ).JsBool ( );
            }
            if ( tree.SubRuleTrees != null ) {
                var b = from d in tree.SubRuleTrees
                        let tmp = FormatDetailTree ( d , control, label , nullable )
                        where !string.IsNullOrWhiteSpace ( tmp )
                        select tmp;

                details.Add ( RuleKeys.SubRule , string.Format ( "[{0}]", string.Join ( " , " , b.ToArray ( ) ) ) );
            }


            var a = details
                .OrderBy ( d => d.Key )
                .Select ( d => FormatKV ( d.Key , d.Value ) );

            return string.Format ( "{{ {0} }}" , string.Join ( " , " , a.ToArray ( ) ) );
        }


        internal static string FormatExport(Dictionary<string , Tuple<bool , List<DetailTree>>> treeNodes , Dictionary<string , Tuple<string , string>> map , bool top = false) {
            List<string> s = new List<string> ( );
            foreach ( var key in treeNodes.Keys ) {
                var controls = map.Where ( m => m.Value.Item1 == key ).Select ( m => Tuple.Create( m.Key, m.Value.Item2) ).ToList ( );
                if ( controls.Count ( ) == 0 )
                    continue;

                var details = treeNodes[key].Item2;
                var canNull = treeNodes[key].Item1;
                controls.ForEach ( c => {
                    var a = from d in details
                            let tmp = FormatDetailTree ( d , c.Item1, c.Item2 , canNull , top )
                            where !string.IsNullOrWhiteSpace ( tmp )
                            select tmp;
                    s.AddRange ( a );
                } );
            }
            
            var b = string.Format ( "[ {0} ]" , string.Join ( " , " , s ) );
            return b;
        }




        private static string FormatKV(RuleKeys key , string value , bool quote = false) {
            return string.Format ( quote ? "{0}:'{1}'" : "{0}:{1}" , key.ToString ( ).LowerFirst ( ) , quote ? value.JsSafeString ( ) : value );
        }

        internal static string JsSafeString(this string str) {
            if ( String.IsNullOrEmpty ( str ) )
                return "";//必须这样

            //return str.ToString().Replace("'", "\\'").Replace("\"", "&quot;");
            str = Regex.Replace ( str , "(['\"\\\\])" , @"\$1" );
            return str.Replace ( "\r\n" , "\\r\\n" );
        }

        internal static string JsBool(this bool b) {
            return b ? "true" : "false";
        }

        private static string Repeat(this char str , int count) {
            if ( count < 0 )
                return "";
            return new string ( str , count );
        }

        private static string LowerFirst(this string str) {
            return Regex.Replace ( str , @"^([A-Z])" , (ma) => {
                return ma.Groups[0].Value.ToLower ( );
            } );
        }
    }
}
